QUIC协议分析

您所在的位置:网站首页 quic 组播 QUIC协议分析

QUIC协议分析

2024-05-17 22:05:53| 来源: 网络整理| 查看: 265

quic协议分析

QUIC是由谷歌设计的一种基于UDP的传输层网络协议,并且已经成为IETF草案。HTTP/3就是基于QUIC协议的。QUIC只是一个协议,可以通过多种方法来实现,目前常见的实现有Google的quiche,微软的msquic,mozilla的neqo,以及基于go语言的quic-go等。

由于go语言的简洁性以及编译的便捷性,本文将选用quic-go进行quic协议的分析,该库是完全基于go语言实现,可以用于构建客户端或服务端。

源码编译与测试 下载 从https://golang.org/dl/下载golang编译器,要求go版本为1.14+。 使用git clone https://github.com/lucas-clemente/quic-go.git下载库 编译 服务端 cd example go build main.go

之后使用./main -qlog -v -tcp运行即可。

必须带上-tcp参数是因为浏览器第一次访问时仍然是要通过TCP进行的,如果不带浏览器将无法访问。

客户端

先修改example/client/main.go,在60行之后加上qconf.Versions = []protocol.VersionNumber{protocol.VersionDraft29},选择quic版本为draft-29。

cd example/client go build main.go

之后使用./main -v -insecure -keylog ssl.log https://quic.rocks:4433/即可访问支持quic协议的网站。

服务端测试 浏览器访问

在firefox中打开about:config,搜索HTTP3,将值设为True以打开HTTP3的实验特性。

打开https://localhost:6121/demo/tile网页,通过调试工具查看请求,当第一次请求该网页时,会通过TCP协议进行:

而在响应头中会带上Alt-Svc,以告诉浏览器该服务器支持HTTP3协议:

之后刷新页面,浏览器就会以HTTP3协议来访问:

抓包

使用wireshark对loopback进行抓包,过滤器设置为udp.port==6121,此时wireshark只显示为UDP协议,并未解析为quic,需要右键Decode As解析为quic。

可以看到,第一个包的类型为Initial,进行了0-RTT的初始化。

问题解决

当访问时,服务器可能会报错Client offered version draft-29, sending Version Negotiation,这是因为当使用-tcp选项后,将使用默认设置,而在默认设置中未开启draft-29版本的支持,因此需要修改源码,将internal/protocol/version.go:30中的var SupportedVersions = []VersionNumber{VersionTLS}修改为var SupportedVersions = []VersionNumber{VersionTLS, VersionDraft29}即可。

客户端测试

使用./main -v -insecure -keylog key.log https://quic.rocks:4433/访问测试网站,可以看见最后成功输出了网页的内容 “You have successfully loaded quic.rocks using QUIC!”,使用的协议为HTTP/3,并且错误代码为0x100,即未发生错误。

抓包

在wireshark中的首选项-protocol-tls-(pre)-master-secret log filename设置为上面输出的key.log文件,用来对quic的payload进行解密,之后可以看到客户端的完整的请求过程,包括1-RTT的握手,HTTP3数据发送,断开连接等:

协议分析 数据包

quic的数据包是通过UDP数据报进行传输的,一个数据报中可以包含一个或多个quic数据包。quic数据包编号被分为三个空间:

Initial:所有初始包 Handshake:所有握手包 Application data:所有 0-RTT 和 1-RTT 加密的数据包

从上图的抓包中可以看见三种类型的包:Initial,Handshake以及Protected payload即Application data。

首部

quic首部分为两种:Long header 和 Short Header,通过第一个有效字节的最高位来区分。首部当中有部分字段是于版本有关的,本文将以quic-29为基础进行分析。

Long header的定义如下:

Long Header Packet { Header Form (1) = 1, Fixed Bit (1) = 1, Long Packet Type (2), Type-Specific Bits (4), Version (32), Destination Connection ID Length (8), Destination Connection ID (0..160), Source Connection ID Length (8), Source Connection ID (0..160), }

Long Header Packets的类型包括四种:Initial,0-RTT,Handshake,Retry。

Short Header的定义如下:

Short Header Packet { Header Form (1) = 0, Fixed Bit (1) = 1, Spin Bit (1), Reserved Bits (2), Key Phase (1), Packet Number Length (2), Destination Connection ID (0..160), Packet Number (8..32), Packet Payload (..), }

在版本协商以及1-RTT密钥传输完成后,quic就会使用Short Header Packet来传输数据。

连接迁移 Connection Migration

quic通过在首部携带Connection ID来保证在底层协议(UPD、IP等)寻址发生变化时也能够将数据包分发到正确的端点上。在TCP协议中,是通过四元组(源 IP,源端口,目的 IP,目的端口)来标识连接的,而当网络发生切换时,IP就会发生变化,使得连接需要重新建立,浪费大量时间;而quic通过Connection ID来对连接进行标识,只要ID不变,这条连接就可以保持,这就给quic协议带来了连接迁移的特性。

握手

quic加密握手提供以下属性:

认证密钥交换,其中 服务端总是经过身份验证 客户端可以选择性进行身份验证 每个连接都会产生不同并且不相关的密钥 密钥材料(keying material)可用于 0-RTT 和 1-RTT 数据包的保护 两个端点(both endpoints)传输参数的认证值,以及服务端传输参数的保密保护 应用协议的认证协商(TLS 使用 ALPN)

1-rtt的握手流程如下所示:

Client Server Initial[0]: CRYPTO[CH] -> Initial[0]: CRYPTO[SH] ACK[0] Handshake[0]: CRYPTO[EE, CERT, CV, FIN] Handshake[1]: ACK[0] Initial[0]: CRYPTO[SH] ACK[0] Handshake[0] CRYPTO[EE, FIN] Handshake[1]: ACK[0] dialAddr -> DialAddrEarly -> DialAddrEarlyContext -> dialAddrContext -> dialContext -> newClient -> client.dial -> newClientSession -> session.run -> RunHandshake -> conn.Handshake -> clientHandshake。最终在Conn.clientHandshake函数中完成了握手的设置,之后通过clientHandshakeState.handshark函数完成了发送等工作。

在newClient函数中,通过generateConnectionID和generateConnectionIDForInitial对srcConnID和destConnID进行了生成。

在handshark函数中,调用establishKeys函数,完成了密钥的生成,之后调用sendFinished函数,将Client Hello帧写入到TLS Record层,完成握手包的发送。

数据包的接收

在session.run中的runloop中,通过select对接收通道进行监听,当收到数据包时,就会调用handlePacketImpl -> handleSinglePacket -> handleUnpackedPacket函数进行处理。

在handleUnpackedPacket函数中,如果是第一个包,就会读取其SrcConnectionID,将其设置为该连接的destination connection ID;之后对包中的帧依次进行读取,并使用parseFrame函数进行判断,并调用对应函数进行解析,最后调用handleFrame函数中调用相关函数进行处理。

在握手过程中,接收的第一个Initial包为合并包(coalesced packet),其第一个帧为ACK帧,通过parseAckFrame进行解析,使用handleAckFrame函数进行处理;第二个帧为Crypto帧,消息为Server Hello,通过parseCryptoFrame函数解析,handleCryptoFrame函数进行处理,该函数会通过session.cryptoStreamManager对密钥信息进行处理。之后第二个Handshake包中只有一个Crypto帧,消息类型为Encrypted Extensions。第三个quic包中包含了一个Stream帧,stream id为3,这个帧会通过handleStreamFrameImpl进行处理,在该函数中会将数据push到frameQueue队列中去,之后通过signalRead函数来通知数据包的到达。该帧的内容为HTTP3的SETTINGS帧。

连接建立及HTTP3数据传输

在第二个RTT中,client先通过Initial包发送ACK帧对收到的包进行确认,之后再通过Handshake包发送了CRYPTO帧和ACK帧,此CRYPTO帧的消息为Handshark protocol: Finished。最后再分别发送了Stream id为0和2的HTTP3 HEADERS帧和SETTINGS帧。

Stream id为0的HEADERS包即为http请求,该包使用了QPACK方法进行压缩,该方法与http2的HPACK类似,而根据QPACK的定义,id为2和3的stream分别为encoder stream和decoder stream,即上文中提及的两个SETTINGS帧。

之后client接收到了Handshark包,其中包含一个ACK帧。此时,1-RTT的握手过程已经结束,因此接下来收到的包的类型就变为了Short header packet,收到的第一个包的类型为HANDSHARK_DONE,说明握手完成。

最后,服务端返回了一个HTTP3的DATA帧,该帧中即包含了请求的响应数据,如下图,可以看到数据的对应文本即为html文档。

在收到数据后,客户端就发送了一个CONNECTION_CLOSE的帧关闭连接,Error code为0x100说明正常关闭,未发生错误。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭